{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Serialization example\n", "\n", "This example illustrates how to load a dataset from JSON, run the model on that dataset and write the output back to JSON. At the end the example is also shown for `msgpack`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load dependencies" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import json\n", "import pprint\n", "from pathlib import Path\n", "\n", "from pandas import DataFrame\n", "\n", "from power_grid_model import AttributeType, ComponentAttributeFilterOptions, ComponentType, PowerGridModel\n", "from power_grid_model.utils import json_deserialize, json_serialize" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load a dataset from a JSON file\n", "\n", "The data is in the `data/serialized-input.json` file.\n", "\n", "### Load the JSON file\n", "\n", "This is just for illustration purposes." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'attributes': {'node': ['id', 'u_rated'],\n", " 'source': ['id', 'node', 'status', 'u_ref', 'sk'],\n", " 'sym_load': ['id',\n", " 'node',\n", " 'status',\n", " 'type',\n", " 'p_specified',\n", " 'q_specified']},\n", " 'data': {'line': [{'c1': 4e-05,\n", " 'from_node': 1,\n", " 'from_status': 1,\n", " 'i_n': 500.0,\n", " 'id': 4,\n", " 'r1': 0.11,\n", " 'tan1': 0.1,\n", " 'to_node': 2,\n", " 'to_status': 1,\n", " 'x1': 0.12},\n", " {'c1': 5e-05,\n", " 'from_node': 2,\n", " 'from_status': 1,\n", " 'i_n': 550.0,\n", " 'id': 5,\n", " 'r1': 0.15,\n", " 'tan1': 0.12,\n", " 'to_node': 3,\n", " 'to_status': 1,\n", " 'x1': 0.16}],\n", " 'node': [[1, 10500.0], [2, 10500.0], [3, 10500.0]],\n", " 'source': [[15, 1, 1, 1.03, 1e+20],\n", " [16, 1, 1, 1.04, None],\n", " {'id': 17,\n", " 'node': 1,\n", " 'rx_ratio': 0.2,\n", " 'sk': 10000000000.0,\n", " 'status': 1,\n", " 'u_ref': 1.03}],\n", " 'sym_load': [[7, 2, 1, 0, 1010000.0, 210000.0],\n", " [8, 3, 1, 0, 1020000.0, 220000.0]]},\n", " 'is_batch': False,\n", " 'type': 'input',\n", " 'version': '1.0'}\n" ] } ], "source": [ "with Path(\"data/serialized_input.json\").open() as fp:\n", " data = fp.read()\n", "\n", "pprint.pprint(json.loads(data))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Deserialize the JSON data" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "components: [node, line, source, sym_load]\n" ] }, { "data": { "text/plain": [ "array([(1, 10500.), (2, 10500.), (3, 10500.)],\n", " dtype={'names': [id, u_rated], 'formats': ['\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idu_rated
0110500.0
1210500.0
2310500.0
\n", "" ], "text/plain": [ " id u_rated\n", "0 1 10500.0\n", "1 2 10500.0\n", "2 3 10500.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "dataset = json_deserialize(data)\n", "\n", "print(\"components:\", list(dataset.keys()))\n", "display(dataset[ComponentType.node])\n", "display(DataFrame(dataset[ComponentType.node]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Run power flow calculation on the loaded input data" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idenergizedu_puuu_anglepq
0111.03000010815.000000-2.530317e-142.408998e+06-2.863495e+06
1211.02999710814.968183-4.398000e-03-1.010000e+06-2.100000e+05
2311.02948410809.581008-6.839956e-03-1.020000e+06-2.200000e+05
\n", "
" ], "text/plain": [ " id energized u_pu u u_angle p \\\n", "0 1 1 1.030000 10815.000000 -2.530317e-14 2.408998e+06 \n", "1 2 1 1.029997 10814.968183 -4.398000e-03 -1.010000e+06 \n", "2 3 1 1.029484 10809.581008 -6.839956e-03 -1.020000e+06 \n", "\n", " q \n", "0 -2.863495e+06 \n", "1 -2.100000e+05 \n", "2 -2.200000e+05 " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "model = PowerGridModel(dataset)\n", "output = model.calculate_power_flow()\n", "\n", "display(DataFrame(output[ComponentType.node]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Serialize the output dataset\n", "\n", "### Default format\n", "\n", "By default, the data is formatted nicely" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"version\": \"1.0\",\n", " \"type\": \"sym_output\",\n", " \"is_batch\": false,\n", " \"attributes\": {},\n", " \"data\": {\n", " \"node\": [\n", " {\"id\": 1, \"energized\": 1, \"u_pu\": 1.0300000000010254, \"u\": 10815.000000010767, \"u_angle\": -2.5303169101427072e-14, \"p\": 2408997.8394388668, \"q\": -2863495.3646741668},\n", " {\"id\": 2, \"energized\": 1, \"u_pu\": 1.0299969698156055, \"u\": 10814.968183063858, \"u_angle\": -0.0043979998047547451, \"p\": -1009999.9999999704, \"q\": -210000.00000006545},\n", " {\"id\": 3, \"energized\": 1, \"u_pu\": 1.0294839055693445, \"u\": 10809.581008478117, \"u_angle\": -0.0068399561753802384, \"p\": -1019999.999999999, \"q\": -219999.99999996895}\n", " ],\n", " \"line\": [\n", " {\"id\": 4, \"energized\": 1, \"loading\": 0.39953190919371073, \"p_from\": 2408997.8394388668, \"q_from\": -2863495.3646741668, \"i_from\": 199.76595459685538, \"s_from\": 3742041.7279784, \"p_to\": -2252625.7643675441, \"q_to\": 1403928.5369478234, \"i_to\": 141.69843328389507, \"s_to\": 2654305.5911384653},\n", " {\"id\": 5, \"energized\": 1, \"loading\": 0.19770474338129659, \"p_from\": 1242625.7643675739, \"q_from\": -1613928.5369477719, \"i_from\": 108.73760885971312, \"s_from\": 2036880.9765532378, \"p_to\": -1019999.999999999, \"q_to\": -219999.99999996895, \"i_to\": 55.731992269810064, \"s_to\": 1043455.796859639}\n", " ],\n", " \"source\": [\n", " {\"id\": 15, \"energized\": 1, \"p\": -7836685.7517324276, \"q\": -105348495.34383295, \"i\": 5639.485452974508, \"s\": 105639571.72755387, \"pf\": -0.074183240461664912},\n", " {\"id\": 16, \"energized\": 1, \"p\": 10248883.058391979, \"q\": 102488830.58119535, \"i\": 5498.573991718361, \"s\": 102999999.98954155, \"pf\": 0.099503719023617807},\n", " {\"id\": 17, \"energized\": 1, \"p\": -0.0018078295912785419, \"q\": -0.010408119105616903, \"i\": 5.6394854529745069e-07, \"s\": 0.010563957172755385, \"pf\": -0.17113185539420431}\n", " ],\n", " \"sym_load\": [\n", " {\"id\": 7, \"energized\": 1, \"p\": 1010000, \"q\": 210000, \"i\": 55.071353939559145, \"s\": 1031600.6979447039, \"pf\": 0.97906098940438901},\n", " {\"id\": 8, \"energized\": 1, \"p\": 1020000, \"q\": 220000, \"i\": 55.731992269810462, \"s\": 1043455.7968596466, \"pf\": 0.97752104408232876}\n", " ]\n", " }\n", "}\n" ] } ], "source": [ "serialized_output = json_serialize(output)\n", "\n", "print(serialized_output)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Compact serialization\n", "\n", "In the full result, all attributes are explicitly listed for each component.\n", "In addition, all attributes are listed on a separate whiteline.\n", "This is fairly expensive memory-wise.\n", "\n", "If you need a more memory-efficient output, you can tell the serializer to use compact lists and to avoid using redundant newlines and whitespaces." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\"version\":\"1.0\",\"type\":\"sym_output\",\"is_batch\":false,\"attributes\":{\"node\":[\"id\",\"energized\",\"u_pu\",\"u\",\"u_angle\",\"p\",\"q\"],\"line\":[\"id\",\"energized\",\"loading\",\"p_from\",\"q_from\",\"i_from\",\"s_from\",\"p_to\",\"q_to\",\"i_to\",\"s_to\"],\"source\":[\"id\",\"energized\",\"p\",\"q\",\"i\",\"s\",\"pf\"],\"sym_load\":[\"id\",\"energized\",\"p\",\"q\",\"i\",\"s\",\"pf\"]},\"data\":{\"node\":[[1,1,1.0300000000010254,10815.000000010767,-2.5303169101427072e-14,2408997.8394388668,-2863495.3646741668],[2,1,1.0299969698156055,10814.968183063858,-0.0043979998047547451,-1009999.9999999704,-210000.00000006545],[3,1,1.0294839055693445,10809.581008478117,-0.0068399561753802384,-1019999.999999999,-219999.99999996895]],\"line\":[[4,1,0.39953190919371073,2408997.8394388668,-2863495.3646741668,199.76595459685538,3742041.7279784,-2252625.7643675441,1403928.5369478234,141.69843328389507,2654305.5911384653],[5,1,0.19770474338129659,1242625.7643675739,-1613928.5369477719,108.73760885971312,2036880.9765532378,-1019999.999999999,-219999.99999996895,55.731992269810064,1043455.796859639]],\"source\":[[15,1,-7836685.7517324276,-105348495.34383295,5639.485452974508,105639571.72755387,-0.074183240461664912],[16,1,10248883.058391979,102488830.58119535,5498.573991718361,102999999.98954155,0.099503719023617807],[17,1,-0.0018078295912785419,-0.010408119105616903,5.6394854529745069e-07,0.010563957172755385,-0.17113185539420431]],\"sym_load\":[[7,1,1010000,210000,55.071353939559145,1031600.6979447039,0.97906098940438901],[8,1,1020000,220000,55.731992269810462,1043455.7968596466,0.97752104408232876]]}}\n" ] } ], "source": [ "serialized_output = json_serialize(output, use_compact_list=True, indent=-1)\n", "\n", "print(serialized_output)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The compact result is still valid JSON" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'attributes': {'line': ['id',\n", " 'energized',\n", " 'loading',\n", " 'p_from',\n", " 'q_from',\n", " 'i_from',\n", " 's_from',\n", " 'p_to',\n", " 'q_to',\n", " 'i_to',\n", " 's_to'],\n", " 'node': ['id', 'energized', 'u_pu', 'u', 'u_angle', 'p', 'q'],\n", " 'source': ['id', 'energized', 'p', 'q', 'i', 's', 'pf'],\n", " 'sym_load': ['id', 'energized', 'p', 'q', 'i', 's', 'pf']},\n", " 'data': {'line': [[4,\n", " 1,\n", " 0.39953190919371073,\n", " 2408997.839438867,\n", " -2863495.364674167,\n", " 199.76595459685538,\n", " 3742041.7279784,\n", " -2252625.764367544,\n", " 1403928.5369478234,\n", " 141.69843328389507,\n", " 2654305.5911384653],\n", " [5,\n", " 1,\n", " 0.1977047433812966,\n", " 1242625.764367574,\n", " -1613928.536947772,\n", " 108.73760885971312,\n", " 2036880.9765532378,\n", " -1019999.999999999,\n", " -219999.99999996895,\n", " 55.731992269810064,\n", " 1043455.796859639]],\n", " 'node': [[1,\n", " 1,\n", " 1.0300000000010254,\n", " 10815.000000010767,\n", " -2.5303169101427072e-14,\n", " 2408997.839438867,\n", " -2863495.364674167],\n", " [2,\n", " 1,\n", " 1.0299969698156055,\n", " 10814.968183063858,\n", " -0.004397999804754745,\n", " -1009999.9999999704,\n", " -210000.00000006545],\n", " [3,\n", " 1,\n", " 1.0294839055693445,\n", " 10809.581008478117,\n", " -0.0068399561753802384,\n", " -1019999.999999999,\n", " -219999.99999996895]],\n", " 'source': [[15,\n", " 1,\n", " -7836685.751732428,\n", " -105348495.34383295,\n", " 5639.485452974508,\n", " 105639571.72755387,\n", " -0.07418324046166491],\n", " [16,\n", " 1,\n", " 10248883.058391979,\n", " 102488830.58119535,\n", " 5498.573991718361,\n", " 102999999.98954155,\n", " 0.0995037190236178],\n", " [17,\n", " 1,\n", " -0.001807829591278542,\n", " -0.010408119105616903,\n", " 5.639485452974507e-07,\n", " 0.010563957172755385,\n", " -0.1711318553942043]],\n", " 'sym_load': [[7,\n", " 1,\n", " 1010000,\n", " 210000,\n", " 55.071353939559145,\n", " 1031600.6979447039,\n", " 0.979060989404389],\n", " [8,\n", " 1,\n", " 1020000,\n", " 220000,\n", " 55.73199226981046,\n", " 1043455.7968596466,\n", " 0.9775210440823288]]},\n", " 'is_batch': False,\n", " 'type': 'sym_output',\n", " 'version': '1.0'}\n" ] } ], "source": [ "pprint.pprint(json.loads(serialized_output))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Msgpack serialization\n", "\n", "To have even higher performance and smaller space, you can use the binary format [`msgpack`](https://msgpack.org/). The example below shows a round trip to dump and load `msgpack` data, and intantiate model." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Serialize to msgpack\n", "\n", "We can serialize the output data into `msgpack` binary with and without compact list. The result is a `bytes` object. See the resulted differences in length of the data. The differences will be significant when you have a large dataset." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Type of the returned objects: , \n", "Length of not-compact data: 993\n", "Length of compact data: 818\n" ] } ], "source": [ "from power_grid_model.utils import msgpack_serialize\n", "\n", "msgpack_data_not_compact = msgpack_serialize(output, use_compact_list=False)\n", "msgpack_data_compact = msgpack_serialize(output, use_compact_list=True)\n", "\n", "print(f\"Type of the returned objects: {type(msgpack_data_not_compact)}, {type(msgpack_data_compact)}\")\n", "print(f\"Length of not-compact data: {len(msgpack_data_not_compact)}\")\n", "print(f\"Length of compact data: {len(msgpack_data_compact)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Deserialize from msgpack\n", "\n", "We can deserialize the data we just created. We then print the node result. Note that they are exactly the same." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "----Node result from not compact data----\n", " id energized u_pu u u_angle p \\\n", "0 1 1 1.030000 10815.000000 -2.530317e-14 2.408998e+06 \n", "1 2 1 1.029997 10814.968183 -4.398000e-03 -1.010000e+06 \n", "2 3 1 1.029484 10809.581008 -6.839956e-03 -1.020000e+06 \n", "\n", " q \n", "0 -2.863495e+06 \n", "1 -2.100000e+05 \n", "2 -2.200000e+05 \n", "----Node result from compact data----\n", " id energized u_pu u u_angle p \\\n", "0 1 1 1.030000 10815.000000 -2.530317e-14 2.408998e+06 \n", "1 2 1 1.029997 10814.968183 -4.398000e-03 -1.010000e+06 \n", "2 3 1 1.029484 10809.581008 -6.839956e-03 -1.020000e+06 \n", "\n", " q \n", "0 -2.863495e+06 \n", "1 -2.100000e+05 \n", "2 -2.200000e+05 \n" ] } ], "source": [ "from power_grid_model.utils import msgpack_deserialize\n", "\n", "output_data_not_compact = msgpack_deserialize(msgpack_data_not_compact)\n", "output_data_compact = msgpack_deserialize(msgpack_data_compact)\n", "\n", "print(\"----Node result from not compact data----\")\n", "print(DataFrame(output_data_not_compact[ComponentType.node]))\n", "print(\"----Node result from compact data----\")\n", "print(DataFrame(output_data_compact[ComponentType.node]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Selective deserialization and dataset format\n", "\n", "To control the dataset returned by the deserialization functionality, you can use the `data_filter` argument.\n", "\n", "### Deserialization to columnar dataset\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "components: [node, line, source, sym_load]\n" ] }, { "data": { "text/plain": [ "{id: array([1, 2, 3], dtype=int32), u_rated: array([10500., 10500., 10500.])}" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "dataset = json_deserialize(data, data_filter=ComponentAttributeFilterOptions.everything)\n", "\n", "print(\"components:\", list(dataset.keys()))\n", "display(dataset[ComponentType.node])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Deserialized data format selection per component type\n", "\n", "To select specific components and data formats for the deserialized data, provide a dictionary of components and their desired output types to the `data_filter`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "components: [node, line, source, sym_load]\n", "node attributes: [id, u_rated]\n", "source attributes: [id, node, status, u_ref, sk]\n", "sym_load attributes: [id, node, status, type, p_specified, q_specified]\n", "line attributes: [id, from_node, to_node, from_status, to_status, r1, x1, c1, tan1, i_n]\n" ] } ], "source": [ "dataset = json_deserialize(\n", " data,\n", " data_filter={\n", " ComponentType.node: None, # nodes in a row-based data format\n", " ComponentType.source: [\n", " AttributeType.id,\n", " AttributeType.node,\n", " AttributeType.status,\n", " AttributeType.u_ref,\n", " AttributeType.sk,\n", " ], # only specific attributes\n", " ComponentType.sym_load: ComponentAttributeFilterOptions.everything, # all attributes as columns\n", " ComponentType.line: ComponentAttributeFilterOptions.relevant, # only attributes that are not null/nan\n", " },\n", ")\n", "\n", "print(\"components:\", list(dataset.keys()))\n", "print(\"node attributes:\", list(dataset[ComponentType.node].dtype.names))\n", "print(\"source attributes:\", list(dataset[ComponentType.source].keys()))\n", "print(\"sym_load attributes:\", list(dataset[ComponentType.sym_load].keys()))\n", "print(\"line attributes:\", list(dataset[ComponentType.line].keys()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A columnar dataset can also be serialized again, as one would expect." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"version\": \"1.0\",\n", " \"type\": \"sym_output\",\n", " \"is_batch\": false,\n", " \"attributes\": {},\n", " \"data\": {\n", " \"node\": [\n", " {\"id\": 1, \"energized\": 1, \"u_pu\": 1.0300000000010254, \"u\": 10815.000000010767, \"u_angle\": -2.5303169101427072e-14, \"p\": 2408997.8394388668, \"q\": -2863495.3646741668},\n", " {\"id\": 2, \"energized\": 1, \"u_pu\": 1.0299969698156055, \"u\": 10814.968183063858, \"u_angle\": -0.0043979998047547451, \"p\": -1009999.9999999704, \"q\": -210000.00000006545},\n", " {\"id\": 3, \"energized\": 1, \"u_pu\": 1.0294839055693445, \"u\": 10809.581008478117, \"u_angle\": -0.0068399561753802384, \"p\": -1019999.999999999, \"q\": -219999.99999996895}\n", " ],\n", " \"line\": [\n", " {\"id\": 4, \"energized\": 1, \"loading\": 0.39953190919371073, \"p_from\": 2408997.8394388668, \"q_from\": -2863495.3646741668, \"i_from\": 199.76595459685538, \"s_from\": 3742041.7279784, \"p_to\": -2252625.7643675441, \"q_to\": 1403928.5369478234, \"i_to\": 141.69843328389507, \"s_to\": 2654305.5911384653},\n", " {\"id\": 5, \"energized\": 1, \"loading\": 0.19770474338129659, \"p_from\": 1242625.7643675739, \"q_from\": -1613928.5369477719, \"i_from\": 108.73760885971312, \"s_from\": 2036880.9765532378, \"p_to\": -1019999.999999999, \"q_to\": -219999.99999996895, \"i_to\": 55.731992269810064, \"s_to\": 1043455.796859639}\n", " ],\n", " \"source\": [\n", " {\"id\": 15, \"energized\": 1, \"p\": -7836685.7517324276, \"q\": -105348495.34383295, \"i\": 5639.485452974508, \"s\": 105639571.72755387, \"pf\": -0.074183240461664912},\n", " {\"id\": 16, \"energized\": 1, \"p\": 10248883.058391979, \"q\": 102488830.58119535, \"i\": 5498.573991718361, \"s\": 102999999.98954155, \"pf\": 0.099503719023617807},\n", " {\"id\": 17, \"energized\": 1, \"p\": -0.0018078295912785419, \"q\": -0.010408119105616903, \"i\": 5.6394854529745069e-07, \"s\": 0.010563957172755385, \"pf\": -0.17113185539420431}\n", " ],\n", " \"sym_load\": [\n", " {\"id\": 7, \"energized\": 1, \"p\": 1010000, \"q\": 210000, \"i\": 55.071353939559145, \"s\": 1031600.6979447039, \"pf\": 0.97906098940438901},\n", " {\"id\": 8, \"energized\": 1, \"p\": 1020000, \"q\": 220000, \"i\": 55.731992269810462, \"s\": 1043455.7968596466, \"pf\": 0.97752104408232876}\n", " ]\n", " }\n", "}\n" ] } ], "source": [ "serialized_output = json_serialize(output)\n", "\n", "print(serialized_output)" ] } ], "metadata": { "kernelspec": { "display_name": "power-grid-model", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.3" } }, "nbformat": 4, "nbformat_minor": 4 }